package dht.identifier.identifier; /**
 * File:   ByteArrayIdentifier.java
 * Author: Gvennet
 */

import java.util.Arrays;
import java.util.Random;

/**
 * Provides a byte-array identifier.
 */
public final class ByteArrayIdentifier implements Identifier
{

	private final byte[] data;

	private static final char[] HEX = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};


	/**
	 * Creates a new random <code>Identifier</code>.
	 *
	 * @param length the <code>Identifier</code> length in bytes
	 * @return a new random <code>Identifier</code>.
	 */
	public static Identifier randomIdentifier(int length)
	{
		Random random = new Random();
		byte[] data = new byte[length];
		random.nextBytes(data);
		return new ByteArrayIdentifier(data);
	}

	/**
	 * Creates a new random <code>Identifier</code>.
	 *
	 * @param length the <code>Identifier</code> length in bytes
	 * @param random the random generator
	 * @return a new random <code>Identifier</code>.
	 */
	public static Identifier randomIdentifier(int length, Random random)
	{
		byte[] data = new byte[length];
		random.nextBytes(data);
		return new ByteArrayIdentifier(data);
	}

	/**
	 * Provides the byte array representation of this <code>Identifier</code>.
	 *
	 * @return a new byte array which is the byte array representation of this <code>Identifier</code>.
	 */

	//@Override
	public byte[] getByteArray()
	{
		return Arrays.copyOf(data, data.length);
	}

	/**
	 * Creates a new <code>ByteArrayIdentifier</code> object.
	 *
	 * @param data the <code>Identifier</code> data.
	 * @throws IllegalArgumentException if <code>data</code> is <code>null</code>
	 */
	public ByteArrayIdentifier(byte[] data)
	{
		if (data == null)
		{
			throw new IllegalArgumentException("data is null");
		}
		this.data = data.clone();
	}


	/**
	 * Returns the <code>Identifier</code> length.
	 *
	 * @return the <code>Identifier</code> length
	 */
	//@Override
	public int getLength()
	{
		return data.length;
	}

	/**
	 * Evaluates a distance to the <code>Identifier</code>
	 *
	 * @param identifier the <code>Identifier</code> to evaluate distance to
	 * @return a distance to the <code>Identifier</code>
	 * @throws NullPointerException	 if <code>identifier</code> is <code>null</code>
	 * @throws IllegalArgumentException if <code>identifier</code> has different length
	 */
	//@Override
	public Identifier getDistance(Identifier identifier)
	{
		if (identifier == null)
		{
			throw new NullPointerException("identifier is null");
		}
		if (!(identifier instanceof ByteArrayIdentifier))
		{
			throw new IllegalArgumentException("invalid identifier type");
		}
		ByteArrayIdentifier id = (ByteArrayIdentifier) identifier;
		if (data.length != id.data.length)
		{
			throw new IllegalArgumentException("invalid identifier length");
		}
		byte[] distance = new byte[data.length];
		for (int i = 0; i < distance.length; i++)
		{
			distance[i] = (byte) (data[i] ^ id.data[i]);
		}
		return new ByteArrayIdentifier(distance);
	}

	/**
	 * Compares two identifiers by its byte arrays.
	 *
	 * @param identifier the <code>Identifier</code> to be compared
	 * @return 0 if identifiers have the same byte arrays. -1 if <code>Identifier</code> to be compared is bigger than
	 *         this <code>Identifier</code>. 1 if <code>Identifier</code> to be compared is smaller than this
	 *         <code>Identifier</code>.
	 */


	//@Override
	public int compareTo(Identifier identifier)
	{
		if (identifier == null)
		{
			throw new NullPointerException("identifier is null");
		}
		if (!(identifier instanceof ByteArrayIdentifier))
		{
			throw new IllegalArgumentException("invalid identifier type");
		}
		ByteArrayIdentifier id = (ByteArrayIdentifier) identifier;
		if (data.length != id.data.length)
		{
			throw new IllegalArgumentException("invalid identifier length");
		}
		for (int i = 0; i < data.length; i++)
		{
			int a = data[i] & 0xff;
			int b = id.data[i] & 0xff;
			if (a < b)
			{
				return -1;
			}
			else if (a > b)
			{
				return 1;
			}
		}
		return 0;
	}

	/**
	 * Returns the hashCode of this <code>Identifier</code>.
	 *
	 * @return the hashCode of this <code>Identifier</code>.
	 */


	@Override
	public int hashCode()
	{
		return Arrays.hashCode(data);
	}

	/**
	 * Determins if this <code>Identifier</code> is equal to given.
	 *
	 * @param obj the Object to compared.
	 * @return <code>true</code> if this <code>Identifier</code>
	 */

	@Override
	public boolean equals(Object obj)
	{
		if (this == obj)
		{
			return true;
		}
		else if (obj instanceof ByteArrayIdentifier)
		{
			ByteArrayIdentifier id = (ByteArrayIdentifier) obj;
			return Arrays.equals(data, id.data);
		}
		else
		{
			return false;
		}
	}

	@Override
	public String toString()
	{
		StringBuilder buffer = new StringBuilder(data.length * 2);
		for (byte b : data)
		{
			buffer.append(HEX[(b & 0xF0) >>> 4]);
			buffer.append(HEX[b & 0x0F]);
		}
		return buffer.toString();
	}

}

